package com.ushine.web.template.test.client;

import com.ushine.web.template.test.constant.BorderConstant;
import com.ushine.web.template.test.constant.LengthConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import java.lang.reflect.Field;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: yls
 * @Date 2022/3/15 17:10
 * @Description: 对象类型的参数组装
 * @Version 1.0
 **/
@Slf4j
@Component
public class ObjectClient<T> {

    @Resource
    private StringClient stringClient;

    @Resource
    private NumberClient numberClient;

    /**
     * 过滤字段
     */
    private static Map<String, Boolean> filterMap = new HashMap<>();

    private static Random random;

    static {
        filterMap.put("id", true);
        filterMap.put("password",true);
        filterMap.put("deleted", true);
        filterMap.put("createTime", true);
        filterMap.put("createUserName", true);
        filterMap.put("createUserId", true);
        filterMap.put("lastUpdateTime", true);
        filterMap.put("lastUpdateUser", true);

        try {
           random = SecureRandom.getInstanceStrong();
        } catch (NoSuchAlgorithmException e) {
            log.info("随时数类初始化异常：", e);
        }
    }


    /**
     * 功能描述:
     * 〈 遍历所有的字段，组装测试数据 〉
     *
     * @param clazz 待测试的实体
     * @return : java.util.List<T>
     * @author : yls
     * @date : 2022/3/15 17:16
     */
    public List<T> listObjectData(Class<T> clazz) throws IllegalAccessException, InstantiationException {
        // 获取各个字段的随机值
        Map<Field, Map<String, ?>> fieldsMap = getFieldValueMap(clazz);
        // 组装正常对象
        AtomicInteger maxLength = new AtomicInteger(0);
        T t = getNormalObj(clazz, fieldsMap, maxLength);
        int maxSize = maxLength.get();
        List<T> objects = new ArrayList<>(maxSize);
        objects.add(t);
        // 组装随机数据：每个字段填充随机值
        Map<Field, List<?>> valuesMap = new HashMap<>(fieldsMap.size());
        fieldsMap.forEach((k, v) -> valuesMap.put(k, new ArrayList<>(v.values())));
        for (int i = 0; i < maxSize; i++) {
            T temp = clazz.newInstance();
            int finalI = i;
            valuesMap.forEach((field, v) -> {
                field.setAccessible(true);
                try {
                    int size = v.size();
                    if (size == 1) {
                        field.set(temp, v.get(0));
                    } else if (finalI >= size - 1) {
                        field.set(temp, v.get(random.nextInt(size - 1)));
                    } else {
                        field.set(temp, v.get(finalI));
                    }
                } catch (IllegalAccessException e) {
                    log.info("组装对象失败", e);
                }
            });
            objects.add(temp);
        }
        // 添加一个空对象
        objects.add(null);
        return objects;
    }

    private T getNormalObj(Class<T> clazz, Map<Field, Map<String, ?>> fieldsMap, AtomicInteger maxLength) throws InstantiationException, IllegalAccessException {
        /*
         * 获取最大长度的map ，以该值来作为生成的数据量
         * 同时生成一条正常的数据
         */
        T t = clazz.newInstance();
        fieldsMap.forEach((field, v) -> {
            int temp = Math.max(maxLength.get(), v.size());
            maxLength.set(temp);
            try {
                field.setAccessible(true);
                field.set(t, v.get(BorderConstant.NORMAL_VALUE));
            } catch (IllegalAccessException e) {
                log.info("组装测试数据异常", e);
            }
        });
        return t;
    }

    /**
     * 功能描述:
     * 〈 生成字段的随机数据 〉
     *
     * @param clazz 类型
     * @return : java.util.Map<java.lang.String,java.util.Map<java.lang.String,?>>
     * @author : yls
     * @date : 2022/3/16 11:32
     */
    private Map<Field, Map<String, ?>> getFieldValueMap(Class<T> clazz) {
        Field[] declaredFields = clazz.getDeclaredFields();
        Map<Field, Map<String, ?>> fieldsMap = new HashMap<>(10);
        for (Field field : declaredFields) {
            String name = field.getName();
            if (filterMap.containsKey(name)) {
                continue;
            }
            // 判断字段类型
            Class<?> type = field.getType();
            /*
             * 生成字符串类型的随机数据
             */
            if (type == String.class) {
                // 最大长度
                int maxLength = LengthConstant.STR_MAX_LENGTH;
                // 最小长度
                int minLength = LengthConstant.STR_MIN_LENGTH;
                // 获取对应的注解
                Size sizeAnnotation = field.getAnnotation(Size.class);
                if (sizeAnnotation != null) {
                    maxLength = sizeAnnotation.max();
                    minLength = sizeAnnotation.min();
                }
                Map<String, String> strMap = stringClient.getStringData(maxLength, minLength);
                fieldsMap.put(field, strMap);
            }

            /*
             * 生成数值类型的数据
             */
            boolean isNumber = type == Integer.class || type == Long.class
                    || type == Float.class || type == Double.class;
            if (Boolean.TRUE.equals(isNumber)) {
                // 最大长度
                int maxValue = LengthConstant.NUMBER_MAX_VALUE;
                // 最小长度
                int minValue = LengthConstant.NUMBER_MIN_VALUE;
                // 获取对应的注解
                Max maxAnnotation = field.getAnnotation(Max.class);
                if (maxAnnotation != null) {
                    maxValue = (int) maxAnnotation.value();
                }
                Min minAnnotation = field.getAnnotation(Min.class);
                if (minAnnotation != null) {
                    minValue = (int) minAnnotation.value();
                }
                Map<String, Number> numberMap = numberClient.getNumberData(type, minValue, maxValue);
                fieldsMap.put(field, numberMap);
            }

            /*
             * 布尔类型
             */
            if (type == Boolean.class) {
                Map<String, Boolean> booleanMap = new HashMap<>(1);
                booleanMap.put(BorderConstant.NORMAL_VALUE, true);
                fieldsMap.put(field, booleanMap);
            }
        }
        return fieldsMap;
    }

}

//                String methodName = "set" + fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
//                Method method =  clazz.getDeclaredMethod(methodName);
//                method.invoke(t, v.get(BorderConstants.NORMAL_VALUE));
